from time import time
from types import FunctionType

_time = time()
__all__ = [
    "TT",
    "Timer",
    "Printer",
]


def tocPrint(*_):
    dTime = time() - TT.startTime
    TT._totalTime += dTime
    print(f"历时 {dTime:9.6f} 秒。")
    return dTime


class TT:
    _startTime = time()
    _totalTime = 0

    def start(*_):
        TT.startTime = time()

    tic = type("Tic", (), {"__get__": start})()
    toc = type("Toc", (), {"__get__": tocPrint})()

    @classmethod
    def total(cls):
        print(cls._totalTime)


class TicToc:
    __slots__ = "start", "tics", "tocs", "cost"

    def __init__(self):
        self.start = True
        self.tocs, self.cost, self.tics = [], [], [time()]

    def tic(self) -> None:
        if self.start:
            self.tocs.append(time())
            self.cost.append(self.tocs[-1] - self.tics[-1])
        else:
            self.start = True
        self.tics.append(time())

    def toc(self) -> float:
        if self.start:
            self.tocs.append(time())
            self.cost.append(self.tocs[-1] - self.tics[-1])
            self.start = False
        return time() - self.tics[-1]

    def __str__(self):
        return f"Totally cost {self.total():9.6f} / {len(self):6d}"

    def __len__(self):
        return len(self.cost)

    def read(self) -> None:
        print(''.join(f"历时 {t:9.6f} 秒。\n" for t in self.cost()))

    def total(self) -> float:
        return sum(self.cost)

    def beginning(self) -> float:
        return self.tics[0] - _time

    def last(self) -> float:
        return (time() if self.start else self.tocs[-1]) - _time


class Timer:
    """计时器类"""

    start = _time
    current = None
    ticDict: "dict[str, TicToc]" = {None: TicToc()}

    @classmethod
    def tic(cls, key: str = None) -> None:
        cls.ticDict[cls.current].toc()
        cls.current = key
        if key in cls.ticDict:
            cls.ticDict[key].tic()
        else:
            cls.ticDict[key] = TicToc()

    @classmethod
    def total(cls) -> None:
        cls.ticDict[cls.current].toc()
        print(f"Ticking {cls.current} now.")
        totally = [
            (key, tictoc.total(), str(tictoc)) for key, tictoc in cls.ticDict.items()
        ]
        total = sum(map(lambda x: x[1], totally))
        totally.sort(key=lambda x: x[1])
        allTime = time() - cls.start
        for key, t, rep in totally:
            print(f"{rep} $ {t/allTime:6.2%} for {key}.")
        print(f"Totally cost {total:9.6f} $ {total/allTime:6.2%} for all recorded.")
        print(f"Totally cost {allTime:9.6f} for ALL.")

    @classmethod
    def timeIt(cls, key: str):
        def decorate(func):
            def timer(*args, **kwargs):
                current = cls.current
                cls.tic(key)
                ret = func(*args, **kwargs)
                cls.tic(current)
                return ret

            return timer

        return decorate

    @classmethod
    def timeItClass(cls, key: str):
        return lambda obj: type(
            obj.__name__,
            obj.__bases__,
            {
                k: cls.timeIt(key)(elem) if isinstance(elem, FunctionType) else elem
                for k, elem in obj.__dict__.items()
                if k not in obj.__dict__.get("__slots__", ())
            },
        )


class Printer:
    @staticmethod
    def record(func):
        def inner(*lst, **dct):
            print(lst)
            print(dct)
            ret = func(*lst, **dct)
            print(ret)
            return ret

        return inner
